package matrix;

import java.util.Collection;
import java.util.Iterator;

/**
 * Абстрактная {@linkplain matrix.IRange Область}
 * <p>При наследлвании необходимо рализовать следующие методы
 * <li> {@linkplain matrix.IRange#range range} интерфейса {@linkplain matrix.IRange IRange}
 * <li> {@linkplain java.util.Collection#add(Object) add(Object)} интерфейса {@linkplain java.util.Collection Collection}.
 * </p>
 * <p>При необходимости можно перекрыть следующие методы интерфейса {@linkplain java.util.Collection Collection},
 * которые по умолчанию выдают {@linkplain java.lang.UnsupportedOperationException UnsupportedOperationException}
 * <li>{@linkplain java.util.Collection#addAll(Collection) addAll(Collection)}
 * <li>{@linkplain java.util.Collection#contains(Object) contains(Object)}
 * <li>{@linkplain java.util.Collection#containsAll(Collection) containsAll(Collection)}
 * <li>{@linkplain java.util.Collection#equals(Object) equals(Object)}
 * <li>{@linkplain java.util.Collection#hashCode() hashCode()}
 * <li>{@linkplain java.util.Collection#isEmpty() isEmpty()}
 * <li>{@linkplain java.util.Collection#remove(Object) remove(Object)}
 * <li>{@linkplain java.util.Collection#retainAll(Collection) retainAll(Collection)}
 * <li>{@linkplain java.util.Collection#size() size()}
 * <li>{@linkplain java.util.Collection#toArray() toArray()}
 * <li>{@linkplain java.util.Collection#toArray(Object[]) toArray(Object[])}
 * <p>
 * По умолчанию Абстрактная {@linkplain matrix.IRange Область} использует итератор {@linkplain matrix.CSimpleRangeIterator CSimpleRangeIteratror}.
 * Это можно подправить, создав свой класс, реализующий интерфейс {@linkplain matrix.IRangeIterator IRangeIterator} 
 * </p>
 * @see {@linkplain matrix.CAbstractRange#iterator() iterator()} 
 */
public abstract class CAbstractRange implements IRange {
	private IRange m_oParent;
	private int m_iRow;
	private int m_iColumn;
	private int m_iRows;
	private int m_iColumns;
	private ICellFactory m_oCellFactory;
	
	/**
	 * Создаёт новую {@linkplain matrix.IRange Область} указанного размера и заполняет её заданными {@linkplain matrix.ICell Ячейками}
	 * @param aRows количество строк
	 * @param aColumns количество столбцов
	 * @param aCellFactory {@linkplain matrix.ICellFactory Фабрика} {@linkplain matrix.ICell Ячеек}, которыми необходимо заполнить {@linkplain matrix.IRange Область}
	 */
	public CAbstractRange(int aRows, int aColumns) {
		this.m_oParent = null;
		this.m_iRow = 1;
		this.m_iColumn = 1;
		this.m_iRows = aRows;
		this.m_iColumns = aColumns;
	}

	/**
	 * Создаёт новую {@linkplain matrix.IRange Область} указанного размера внутри заданной (родительской) {@linkplain matrix.IRange Области}
	 * @param aParent родительская {@linkplain matrix.IRange Область}
	 * @param aRow верхняя строка родительской {@linkplain matrix.IRange Области}
	 * @param aColumn левый столбец родительской {@linkplain matrix.IRange Области}
	 * @param aRows количество строк
	 * @param aColumns количество столбцов
	 * @throws IllegalArgumentException когда:
	 * (1) aParent == null
	 * (2) aRow, aColumn, aRows, aColumns меньше 1 или
	 * (3) aRow+aRows-1 больше aParent.{@linkplain matrix.IRange#rows() rows()} или
	 * (4) aColumn+aColumns-1 больше aParent.{@linkplain matrix.IRange#columns() columns()}
	 */
	protected CAbstractRange(IRange aParent, int aRow, int aColumn, int aRows, int aColumns) {
		if (aParent == null) {
			throw new IllegalArgumentException("aParent can't be null");
		}
		if ((aRow < 1) || (aColumn < 1) || (aRows < 1) || (aColumns < 1)) {
			throw new IllegalArgumentException("aRow, aColumn, aRows, aColumns can't be < 1");
		}
		if ((aRow+aRows-1 > aParent.rows()) || (aColumn+aColumns-1 > aParent.columns())) {
			throw new IllegalArgumentException("aRow+aRows-1 (aColumn+aColumns-1) can't be > aParent.rows() (aParent.columns())");
		}
		this.m_oParent = aParent;
		this.m_iRow = aRow;
		this.m_iColumn = aColumn;
		this.m_iRows = aRows;
		this.m_iColumns = aColumns;
		this.m_oCellFactory = null;
	}

	public void initStorage(ICellFactory aCellFactory){
		this.m_oCellFactory = aCellFactory;
		for (int r = 1; r <= m_iRows; r++) {
			for (int c = 1; c <= m_iColumns; c++) {
				add(m_oCellFactory.createCell(r, c));
			}
		}
	}
	
	/*Collection implementation*/	
	@Override
	public abstract boolean add(ICell aCell);
	@Override
	public boolean addAll(Collection<? extends ICell> aC){
		throw new UnsupportedOperationException("CAbstractRange.addAll is not implemented");
	}
	@Override
	public final void clear(){
		if (m_oParent != null) {
			m_oParent.clear();
		}
		
		for (int r = 1; r <= m_iRows; r++) {
			for (int c = 1; c <= m_iColumns; c++) {
				cell(r, c).clear();
			}
		}
	}
	@Override
	public boolean contains(Object aO){
		throw new UnsupportedOperationException("CAbstractRange.contains is not implemented");
	}
	@Override
	public boolean containsAll(Collection<?> aC){
		throw new UnsupportedOperationException("CAbstractRange.containsAll is not implemented");
	}
	@Override
	public boolean isEmpty(){
		throw new UnsupportedOperationException("CAbstractRange.isEmpty is not implemented");
	}
	/**
	 * @return {@linkplain matrix.CSimpleRangeIterator CSimpleRangeIteratror}
	 */
	@Override
	public Iterator<ICell> iterator() {
		return new CSimpleRangeIterator(this);
	}
	@Override
	public boolean remove(Object aO){
		throw new UnsupportedOperationException("CAbstractRange.remove is not implemented");
	}
	@Override
	public boolean removeAll(Collection<?> aC){
		throw new UnsupportedOperationException("CAbstractRange.removeAll is not implemented");
	}
	@Override
	public boolean retainAll(Collection<?> aC){
		throw new UnsupportedOperationException("CAbstractRange.retainAll is not implemented");
	}
	@Override
	public int size(){
		throw new UnsupportedOperationException("CAbstractRange.size is not implemented");
	}
	@Override
	public Object[] toArray(){
		throw new UnsupportedOperationException("CAbstractRange.toArray is not implemented");
	}
	@Override
	public <T> T[] toArray(T[] aA){
		throw new UnsupportedOperationException("CAbstractRange.toArray(Object[]) is not implemented");
	}

	/*IRange implementation*/	
	@Override
	public final IRange parent() {
		return m_oParent;
	}

	@Override
	public final int row() {
		return m_iRow;
	}

	@Override
	public final IRange row(int aRow) {
		return range(aRow, 1, 1, columns());
	}

	@Override
	public final int column() {
		return m_iColumn;
	}

	@Override
	public final IRange column(int aColumn) {
		return range(1, aColumn, rows(), 1);
	}

	@Override
	public final int rows() {
		return m_iRows;
	}

	@Override
	public final int columns() {
		return m_iColumns;
	}

	@Override
	public abstract IRange range(int aRow, int aColumn, int aRows, int aColumns);

	@Override
	public final ICell cell(int aRow, int aColumn) {
		if ((aRow < 1) || (aColumn < 1)) {
			throw new IllegalArgumentException("aRow, aColumn can't be < 1");
		}
		if ((aRow > this.rows()) || (aColumn > this.columns())) {
			throw new IllegalArgumentException("aRow (aColumn) can't be > rows() (columns())");
		}

		if (m_oParent != null) {
			return m_oParent.cell(m_iRow+aRow-1, m_iColumn+aColumn-1);
		}
		
		for (ICell cell: this) {
			if ((cell.row() == aRow) && (cell.column() == aColumn)){
				return cell;
			}
		}

/*		
		Iterator<ICell> it = iterator();
		ICell cell;
		while (it.hasNext()){
			cell = it.next();
			if ((cell.row() == aRow) && (cell.column() == aColumn)){
				return cell;
			}
		}
*/		
		return null;
	}

}
